package nii.yoanncouillec.sparql;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.openrdf.query.BindingSet;
import org.openrdf.query.GraphQuery;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.http.HTTPRepository;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.rdfxml.RDFXMLWriter;

/**
 * Connector to triple store, to query it with regular or graph query.
 * @author Yoann COUILLEC, yoanncouillec@gmail.com
 * @since 2012
 * @version 1.0
 */

public class SparqlQuery {

	/**
	 * Basics headers (RDFS, OWL, RDF, XSD).
	 */
	private static String HEADER = 
			"PREFIX :<http://example.org/owlim#>" +			
					"PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>" +
					"PREFIX owl:<http://www.w3.org/2002/07/owl#>" +
					"PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>" +
					"PREFIX xsd:<http://www.w3.org/2001/XMLSchema#> ";

	/**
	 * Link to the repository.
	 */
	private Repository myRepository;

	/**
	 * Connection.
	 */
	private RepositoryConnection con;

	/**
	 * User's headers.
	 */
	private String queryHeader ;

	/**
	 * Constructor of repository connector.
	 * @param endpoint Endpoint
	 * @throws RepositoryException
	 */
	public SparqlQuery(String endpoint) 
			throws RepositoryException{
		myRepository = new HTTPRepository(endpoint);
		myRepository.initialize();
		queryHeader = "";
		con = myRepository.getConnection();
	}

	/**
	 * Constructor of repository connector.
	 * @param sesameServer Adress of server.
	 * @param repositoryID Name of repository.
	 * @throws RepositoryException
	 */
	public SparqlQuery(String sesameServer, String repositoryID) 
			throws RepositoryException{
		myRepository = new HTTPRepository(sesameServer, repositoryID);
		myRepository.initialize();
		queryHeader = "";
		con = myRepository.getConnection();
	}

	/**
	 * Close the link to the repository.
	 * @throws RepositoryException
	 */
	public void close() throws RepositoryException{
		con.close();
		myRepository.shutDown();
	}

	/**
	 * Send a Sparql query to the endpoint.
	 * @param queryString Sparql request.
	 * @return List of bindings.
	 * @throws RepositoryException
	 * @throws MalformedQueryException
	 * @throws QueryEvaluationException
	 */
	public List<BindingSet> tupleQuery (String queryString) 
			throws RepositoryException, MalformedQueryException, 
			QueryEvaluationException{
		TupleQueryResult result = null;
		TupleQuery tupleQuery = con.prepareTupleQuery(
				QueryLanguage.SPARQL, HEADER + queryHeader + queryString);
		result = tupleQuery.evaluate();
		return toList(result);
	}

	/**
	 * Send a Sparql query to the endpoint.
	 * The result is supposed to be uniq, if not the first binding is returned.
	 * @param queryString
	 * @return One binding.
	 * @throws RepositoryException
	 * @throws MalformedQueryException
	 * @throws QueryEvaluationException
	 */
	public BindingSet tupleQueryUniq (String queryString) 
			throws RepositoryException, MalformedQueryException, 
			QueryEvaluationException{
		TupleQueryResult result = null;
		TupleQuery tupleQuery = con.prepareTupleQuery(
				QueryLanguage.SPARQL, HEADER + queryHeader + queryString);
		result = tupleQuery.evaluate();
		return toList(result).get(0);
	}

	/**
	 * Send a graph query to the endpoint.
	 * @param queryString
	 * @return A string containing the graph in RDF/XML way.
	 * @throws RepositoryException
	 * @throws MalformedQueryException
	 * @throws QueryEvaluationException
	 * @throws RDFHandlerException
	 */
	public String graphQuery (String queryString) 
			throws RepositoryException, MalformedQueryException, 
			QueryEvaluationException, RDFHandlerException {
		StringWriter sw = new StringWriter();
		RDFXMLWriter w = new RDFXMLWriter(sw);
		GraphQuery graphQuery = con.prepareGraphQuery(
				QueryLanguage.SPARQL, HEADER + queryHeader + queryString);
		graphQuery.evaluate(w);
		return sw.toString();
	}

	/**
	 * Transform a TupleQueryResult to a List.
	 * @param result Raw result of the sparql query.
	 * @return Result as a list.
	 * @throws QueryEvaluationException
	 */
	private List<BindingSet> toList (TupleQueryResult result) 
			throws QueryEvaluationException {
		ArrayList<BindingSet> ret = new ArrayList<BindingSet>();
		while (result.hasNext()){
			ret.add(result.next());
		}
		return ret;
	}

	/**
	 * Get the user's headers.
	 * @return User's headers.
	 */
	public String getQueryHeader() {
		return queryHeader;
	}

	/**
	 * Modify of the user's headers.
	 * @param queryHeader Modify the user's headers.
	 */
	public void setQueryHeader(String queryHeader) {
		this.queryHeader = queryHeader;
	}

	/**
	 * Add on more header.
	 * @param header
	 */
	public void addHeader (String header){
		this.queryHeader += " " + header;
	}
}
